package com.gzz.orchid.common.boot.mybatis.utils;

import com.gzz.orchid.common.boot.mybatis.annotation.Table;
import com.gzz.orchid.common.boot.mybatis.auditor.InsertHandler;
import com.gzz.orchid.common.boot.mybatis.auditor.UpdateHandler;
import lombok.experimental.UtilityClass;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.mapping.*;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.type.TypeHandlerRegistry;

import java.lang.reflect.InvocationTargetException;
import java.text.DateFormat;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author higmd
 */
@Slf4j
@UtilityClass
public class MybatisUtils {
    private static final List<ResultMapping> EMPTY_RESULTMAPPING =List.of();
    private static final String resultType = "yyyy-MM-dd HH:mm:ss";
    /**
     * 提取SQL语句
     *
     * @param ms MyBatis映射语句对象，包含SQL映射信息
     * @param invocation      拦截器调用对象，包含方法调用的参数信息
     * @return 解析后的完整SQL语句字符串
     */
    public String extractSql(MappedStatement ms, Invocation invocation) {
        // 获取调用参数
        Object parameter = null;
        if (invocation.getArgs().length > 1) {
            parameter = invocation.getArgs()[1];
        }

        // 获取绑定的SQL对象和配置信息
        BoundSql boundSql = ms.getBoundSql(parameter);
        Configuration configuration = ms.getConfiguration();
        // 解析并返回最终的SQL语句
        return resolveSql(configuration, boundSql);
    }


    /**
     * 解析SQL语句，将参数替换为实际值
     *
     * @param configuration MyBatis配置对象，用于获取类型处理器注册表和创建MetaObject
     * @param boundSql      包含SQL语句和参数映射信息的对象
     * @return 解析后的SQL语句，参数已被实际值替换
     */
    public static String resolveSql(Configuration configuration, BoundSql boundSql) {
        Object parameterObject = boundSql.getParameterObject();
        List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
        String sql = boundSql.getSql().replaceAll("[\\s]+", " ");

        // 如果存在参数映射且参数对象不为空，则进行参数替换
        if (!parameterMappings.isEmpty() && parameterObject != null) {
            TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();

            // 判断参数对象是否有对应的类型处理器
            if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
                sql = sql.replaceFirst("\\?", Matcher.quoteReplacement(resolveParameterValue(parameterObject)));

            } else {
                // 处理复杂对象参数，通过MetaObject获取属性值
                MetaObject metaObject = configuration.newMetaObject(parameterObject);
                Matcher matcher = Pattern.compile("\\?").matcher(sql);
                StringBuilder sqlBuffer = new StringBuilder();

                // 遍历参数映射，逐个替换SQL中的参数占位符
                for (ParameterMapping parameterMapping : parameterMappings) {
                    String propertyName = parameterMapping.getProperty();
                    Object obj = null;

                    // 尝试从MetaObject获取属性值，如果获取不到则从额外参数中获取
                    if (metaObject.hasGetter(propertyName)) {
                        obj = metaObject.getValue(propertyName);
                    } else if (boundSql.hasAdditionalParameter(propertyName)) {
                        obj = boundSql.getAdditionalParameter(propertyName);
                    }

                    // 使用正则匹配器替换参数占位符
                    if (matcher.find()) {
                        matcher.appendReplacement(sqlBuffer, Matcher.quoteReplacement(resolveParameterValue(obj)));
                    }
                }

                // 将剩余的SQL部分追加到结果中
                matcher.appendTail(sqlBuffer);
                sql = sqlBuffer.toString();
            }
        }
        return sql;
    }

    public static void printSqlLog(Configuration configuration, BoundSql boundSql){
        Object parameterObject = boundSql.getParameterObject();
        List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
        String sql= boundSql.getSql().replaceAll("[\\s]+", " ");
        StringBuilder sb=new StringBuilder("==> PARAM:");
        if (!parameterMappings.isEmpty() && parameterObject!=null){
            TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
            if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
                sql = sql.replaceFirst("\\?", parameterObject.toString());
            } else {
                MetaObject metaObject = configuration.newMetaObject(parameterObject);
                //
                for (ParameterMapping parameterMapping : parameterMappings) {
                    String propertyName = parameterMapping.getProperty();
                    if (metaObject.hasGetter(propertyName)) {
                        Object obj = metaObject.getValue(propertyName);
                        String parameterValue = obj.toString();
                        sql = sql.replaceFirst("\\?", parameterValue);
                        sb.append(parameterValue).append("(").append(obj.getClass().getSimpleName()).append("),");
                    } else if (boundSql.hasAdditionalParameter(propertyName)) {
                        Object obj = boundSql.getAdditionalParameter(propertyName);
                        String parameterValue = obj.toString();
                        sql = sql.replaceFirst("\\?", parameterValue);
                        sb.append(parameterValue).append("(").append(obj.getClass().getSimpleName()).append("),");
                    }
                }
            }
            sb.deleteCharAt(sb.length()-1);
        }
        System.out.println("SQL = " + sql);
        System.out.println("参数：" + sb.toString());
    }

    /**
     * 解析参数值并将其转换为字符串表示形式
     *
     * @param obj 需要解析的对象参数
     * @return 转换后的字符串表示形式
     */
    private static String resolveParameterValue(Object obj) {
        // 处理字符序列类型，添加单引号包围
        if (obj instanceof CharSequence) {
            return String.format("'%s'", obj);
        }
        // 处理日期类型，使用中国地区格式化后添加单引号包围
        if (obj instanceof Date) {
            DateFormat formatter = DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, Locale.CHINA);
            return  String.format("'%s'", formatter.format(obj)) ;
        }
        // 处理其他类型，null值返回空字符串，非null值转换为字符串
        return obj == null ? "" : String.valueOf(obj);
    }

    /**
     * 处理UpdateListener
     */
    public static void handleUpdateListener(Object parameter){
        Optional.ofNullable(parameter)
                .map(param->param.getClass().getAnnotation(Table.class))
                .ifPresent(table->{
                    if(table.updateListener() != null) {
                        try {
                            UpdateHandler listener = table.updateListener()
                                    .getDeclaredConstructor()
                                    .newInstance();
                            listener.onUpdate(parameter);
                        } catch (InvocationTargetException | NoSuchMethodException | InstantiationException |
                                 IllegalAccessException e) {
                            throw new RuntimeException(e);
                        }
                    }
                });
    }

    /**
     * 处理InsertListener
     */
    public static void handleInsertListener(Object parameter){
        Optional.ofNullable(parameter)
                .map(param->param.getClass().getAnnotation(Table.class))
                .ifPresent(table->{
                    if(table.insertListener() != null) {
                        try {
                            InsertHandler listener = table.insertListener()
                                    .getDeclaredConstructor()
                                    .newInstance();
                            listener.onInsert(parameter);
                        } catch (InvocationTargetException | NoSuchMethodException | InstantiationException |
                                 IllegalAccessException e) {
                            throw new RuntimeException(e);
                        }
                    }
                });
    }


    private static final Pattern DANGEROUS_PATTERN = Pattern.compile("(?i)\\b(DROP|TRUNCATE|DELETE\\s+FROM)\\b");
    public static void checkSql(String sql, Object parameter) {

        if (DANGEROUS_PATTERN.matcher(sql).find()) {
            log.error("检测到潜在 SQL 注入: {}", sql);
            throw new SecurityException("危险 SQL 操作被拦截: " + sql);
            //throw new RuntimeException("SQL contains dangerous keywords");
        }
        // 检查参数
        if (parameter instanceof String param) {
            if (param.contains(";") || param.contains("--")) {
                log.error("检测到可疑参数: {}", param);
                throw new SecurityException("可疑参数被拦截: " + param);
            }
        }
    }


}
